package com.itstack.springframework.beans.factory.xml;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.itstack.springframework.beans.BeansException;
import com.itstack.springframework.beans.PropertyValue;
import com.itstack.springframework.beans.PropertyValues;
import com.itstack.springframework.beans.factory.config.BeanDefinition;
import com.itstack.springframework.beans.factory.config.BeanReference;
import com.itstack.springframework.beans.factory.support.AbstractBeanDefinitionReader;
import com.itstack.springframework.beans.factory.support.BeanDefinitionRegistry;
import com.itstack.springframework.core.io.Resource;
import com.itstack.springframework.core.io.ResourceLoader;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * ClassName:
 * Description: TODO
 *
 * @author: Guo Shuai
 * @version: 1.0
 * @Date: 2021\11\25 0025
 */
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    public XmlBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry) {
        super(beanDefinitionRegistry);
    }

    public XmlBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry, ResourceLoader resourceLoader) {
        super(beanDefinitionRegistry, resourceLoader);
    }

    @Override
    public void loadBeanDefinitions(Resource resource) throws BeansException {
        try {
            try (InputStream is = resource.getInputStream()) {
                doLoadBeanDefinitions(is);
            }
        } catch (IOException | ClassNotFoundException e) {
            throw new BeansException("IOException parsing XML document from " + resource, e);
        }

    }

    @Override
    public void loadBeanDefinitions(Resource... resources) throws BeansException {
        for (Resource resource : resources) {
            loadBeanDefinitions(resource);
        }
    }

    @Override
    public void loadBeanDefinitions(String location) throws BeansException {
        ResourceLoader resourceLoader = getResourceLoader();
        Resource resource = resourceLoader.getResource(location);
        loadBeanDefinitions(resource);
    }

    @Override
    public void loadBeanDefinitions(String... locations) throws BeansException {
        for (String location : locations) {
            loadBeanDefinitions(location);
        }
    }

    protected void doLoadBeanDefinitions(InputStream is) throws ClassNotFoundException {
        SAXReader reader = new SAXReader();
        Document document = null;
        try {
            document = reader.read(is);
        } catch (DocumentException e) {
            throw new BeansException("xml stream can read exception");
        }

        Element rootElement = document.getRootElement();

        List<Element> elements = rootElement.elements();

        for (Element element : elements) {
            if (!element.getName().equals("bean")) continue;

            Attribute attributeId = element.attribute("id");
            Attribute attributeName = element.attribute("name");
            Attribute attributeClass = element.attribute("class");

            Assert.notNull(attributeClass, "beanClass can not be null");
            Assert.isTrue(attributeId != null || attributeName != null, "attribute of id or attribute of name can not be null");

            String id = attributeId != null ? attributeId.getValue() : "";
            String name = attributeName != null ? attributeName.getValue() : "";

            //加载类型
            Class<?> type = Class.forName(attributeClass.getValue());
            String beanName = StrUtil.isNotEmpty(id) ? id : name;

            Assert.notEmpty(beanName, "beanName can not be null");

            //封装成Beandefinition
            BeanDefinition beanDefinition = new BeanDefinition(type, new PropertyValues());

            List<Element> propertyElements = element.elements();
            for (Element propertyElement : propertyElements) {
                if (!propertyElement.getName().equals("property")) continue;

                Attribute attributePropertyName = propertyElement.attribute("name");
                Attribute attributePropertyValue = propertyElement.attribute("value");
                Attribute attributePropertyRef = propertyElement.attribute("ref");

                Assert.notNull(attributePropertyName, "element property's attribute name can not be null");

                String ref = attributePropertyRef != null ? attributePropertyRef.getValue() : "";
                String value = attributePropertyValue != null ? attributePropertyValue.getValue() : null;

                String propertyName = attributePropertyName.getValue();
                Object propertySetValue = StrUtil.isNotEmpty(ref) ? new BeanReference(ref) : value;

                PropertyValue propertyValue = new PropertyValue(propertyName, propertySetValue);

                beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
            }

            getBeanDefinitionRegistry().registerBeanDefinition(beanName, beanDefinition);
        }
    }
}
